#pragma comment(lib, "crypt32.lib")

#include <stdio.h>
#include <tchar.h>		// _ftprintf
#include <windows.h>
#include <Wincrypt.h>

#define MS_SCARD_PROV_			L"Microsoft Base Smart Card Crypto Provider"
#define MS_SCARD_CONT_			L"Sign Key Container"
//#define MS_SCARD_CONT_			L"Auth Key Container"
//-------------------------------------------------------------------
// This example uses the function MyHandleError, a simple error
// handling function to print an error message and exit 
// the program. 
// For most applications, replace this function with one 
// that does more extensive error reporting.

void MyHandleError(LPTSTR psz)
{
	_ftprintf(stderr, TEXT("An error occurred in the program. \n"));
	_ftprintf(stderr, TEXT("%s\n"), psz);
	_ftprintf(stderr, TEXT("Error number %x.\n"), GetLastError());
	_ftprintf(stderr, TEXT("Program terminating. \n"));
	exit(1);
} // End of MyHandleError.

void    hexPrintFromByteArray(size_t p, BYTE array[], size_t sz)
{
	size_t i;
	for(i = 0; i < sz; i++)
	{
		if ((i == p) && (p != 0)) printf("\n");
		printf("%02X ", array[i]);
	}
	printf("\n");
} // End of hexPrintFromByteArray.

bool    calculateHashOperation(HCRYPTPROV hCryptProv, HCRYPTHASH &hHash, BYTE *pbBuffer, DWORD dwBufferLen)
{
	//-------------------------------------------------------------------
	// Create the hash object.

	if(!CryptCreateHash(
		hCryptProv, 
		CALG_MD5, 
		0, 
		0, 
		&hHash))		// out, The address to which the function copies a handle to the new hash object.
		MyHandleError(TEXT("Error during CryptCreateHash."));

	//-------------------------------------------------------------------
	// Compute the cryptographic hash of the buffer.

	if(!CryptHashData(
		hHash,			// Handle of the hash object.
		pbBuffer,		// A pointer to a buffer that contains the data to be added to the hash object.
		dwBufferLen,	// Number of bytes of data to be added.
		0))
		MyHandleError(TEXT("Error during CryptHashData."));

	BYTE        *pbHash;
	DWORD		dwHashLen;
	DWORD       dwHashLenSize	= sizeof(DWORD);

	if(!CryptGetHashParam(
		hHash,
		HP_HASHSIZE,
		(BYTE *)&dwHashLen,
		&dwHashLenSize,
		0))
		MyHandleError(TEXT("CryptGetHashParam failed to get size."));

	pbHash = (BYTE*)malloc(dwHashLen+1);
	if(pbHash == 0)
		MyHandleError(TEXT("Allocation failed."));

	if(CryptGetHashParam(
		hHash,
		HP_HASHVAL,
		pbHash,
		&dwHashLen,
		0))
	{
		// Print the hash value.
		hexPrintFromByteArray(0, pbHash, dwHashLen);
		return true;
	}
	else
	{
		MyHandleError(TEXT("Error during reading hash value."));
		return false;
	}
}	// end of calculateHashOperations

void	readOperations(HCRYPTPROV hCryptProv, CHAR *pszName)
{
	DWORD cbName;
	//---------------------------------------------------------------
	// Read the name of the SmartCard Reader Name.

	cbName = 1000;
	if(CryptGetProvParam(
		hCryptProv, 
		PP_SMARTCARD_READER, 
		(BYTE*)pszName,		// out
		&cbName,			// inout, A pointer to a DWORD value that specifies the size, in bytes, of the buffer pointed to by the pszName parameter
		0))
	{
		printf("SmartCard Reader Name: %s\n", pszName);
	}
	else
	{
		MyHandleError(TEXT("Error reading SmartCard Reader Name.\n"));
	}

	//---------------------------------------------------------------
	// Read the name of the CSP.
	cbName = 1000;
	if(CryptGetProvParam(
		hCryptProv, 
		PP_NAME, 
		(BYTE*)pszName,		// out
		&cbName,			// inout, A pointer to a DWORD value that specifies the size, in bytes, of the buffer pointed to by the pszName parameter
		0))
	{
		printf("Provider name: %s\n", pszName);
	}
	else
	{
		MyHandleError(TEXT("Error reading CSP name.\n"));
	}

	//---------------------------------------------------------------
	// Read the name of the key container.
	cbName = 1000;
	if(CryptGetProvParam(
		hCryptProv, 
		PP_UNIQUE_CONTAINER,	// The CryptAcquireContext function must work with this container name.
		(BYTE*)pszName, 
		&cbName, 
		0))
	{
		printf("Unique Key Container name: %s\n", pszName);
	}
	else
	{
		MyHandleError(TEXT("Error reading key container name.\n"));
	}
}	// end of readOperations

int main (int argc, char *argv[])
{
	//---------------------------------------------------------------
	// Declare and initialize variables.
	HCRYPTPROV hMCryptProv;
	/*HCRYPTPROV hCryptProv;*/

	//---------------------------------------------------------------
	// Get a handle to the default PROV_RSA_FULL provider.
	/*
	Auth Key Container
	Microsoft Base Smart Card Crypto Provider
	Gemplus USB SmartCard Reader 0
	"\\.\<Reader Name>\<Container Name>"
	"\\.\\Gemplus USB SmartCard Reader 0\\Auth Key Container"
	*/
	if(!CryptAcquireContext(
		&hMCryptProv,	// out, handle of a cryptographic service provider (CSP)
		0,				//MS_SCARD_CONT_,	// When pszContainer is zero, a default key container name is used
		MS_SCARD_PROV,	//_,				// "Microsoft Base Smart Card Cryptographic Provider"
		PROV_RSA_FULL, 
		0))
		MyHandleError(TEXT("CryptAcquireContext failed.\n"));

	//---------------------------------------------------------------
	// Read of parameters

	CHAR pszName[1000];
	readOperations(hMCryptProv, pszName);

	//BYTE *ptr = NULL;
	//CHAR *pszAlgType = NULL;

	//---------------------------------------------------------------
	// Read containers, hCryptProv is riquired
	CHAR	pbData[1000];	// Often allocated dynamically.
	DWORD	dwDataLen;
	DWORD	dwFlags;
	// Enumerate the containers.
	DWORD i;
	for(i=0 ; ; i++) {
		// Set the CRYPT_FIRST flag the first time through the loop.
		if(i == 0)	dwFlags = CRYPT_FIRST;
		else		dwFlags = CRYPT_NEXT;

		// Retrieve information about an container.
		dwDataLen = 1000;

		if(!CryptGetProvParam(hMCryptProv, PP_ENUMCONTAINERS, (LPBYTE)pbData, &dwDataLen, dwFlags))
		{
			if(GetLastError() == ERROR_NO_MORE_ITEMS) {
				// Exit the loop. No more key containers
				printf("i = %d\n", i);		
				break;
			} else {
				printf("Error %x reading algorithm!\n", GetLastError());
				return -1;
			}			
		}
		printf("i = %d : %s\n", i, pbData);
		// sign and verify...

		//}
		HCRYPTPROV hCryptProv;
		if(!CryptAcquireContext(
			&hCryptProv,	// out, handle of a cryptographic service provider (CSP)
			pbData,				//MS_SCARD_CONT_,	// When pszContainer is zero, a default key container name is used
			MS_SCARD_PROV,	//_,				// "Microsoft Base Smart Card Cryptographic Provider"
			PROV_RSA_FULL, 
			0))
			MyHandleError(TEXT("CryptAcquireContext failed.\n"));

		//-------------------------------------------------------------------
		// A cryptographic context and a key container are available.
		// Perform any functions that require a cryptographic provider handle.

		//-------------------------------------------------------------------
		// When the handle is no longer needed, it must be released.

		//-------------------------------------------------------------------
		// Declare and initialize variables.

		//HCRYPTPROV hProv;

		HCRYPTHASH	hHash;
		BYTE		*pbBuffer		= (BYTE *)"The data that is to be hashed and signed.";
		DWORD		dwBufferLen		= strlen((char *)pbBuffer)+1;	// Null terminated

		// hash of message.
		if(!calculateHashOperation(hCryptProv, hHash, pbBuffer, dwBufferLen))	return -1;

		HCRYPTKEY	hPubKey;
		BYTE		*pbSignature;
		DWORD		dwSigLen;
		DWORD		dwKeySpec		= AT_KEYEXCHANGE;	// Identifies the private key to use from the key container. It can be AT_KEYEXCHANGE or AT_SIGNATURE.
		// #define AT_KEYEXCHANGE          1
		// #define AT_SIGNATURE            2
		// get the key
		DWORD		dwBlobLen;
		BYTE		*pbKeyBlob	= NULL;        
		HCRYPTKEY	hKey;

		//-------------------------------------------------------------------
		//Get the public at signature key.
		//This is the public key that will be used by the receiver of the hash to verify the signature.
		//In situations where the receiver could obtain the sender's public key from a certificate,
		//this step would not be needed.

		if(!CryptGetUserKey(   
			hCryptProv,
			dwKeySpec,    
			&hKey))		// out, A pointer to the HCRYPTKEY handle of the retrieved keys
			MyHandleError(TEXT("Error during CryptGetUserKey for signkey."));	// Error # 8009000d AT_SIGNATURE
				
		//-------------------------------------------------------------------
		// Export the public key. Here the public key is exported to a PUBLICKEYBLOB
		// so that the receiver of the signed hash can verify the signature.
		// This BLOB could be written to a file and sent to another user.

		if(CryptExportKey(   
			hKey,    
			NULL,			// If the key BLOB type specified by dwBlobType is PUBLICKEYBLOB, this parameter is unused and must be set to zero
			PUBLICKEYBLOB,
			0,    
			NULL,			// out, pbData
			&dwBlobLen))	// inout, When the function returns, this value contains the number of bytes stored in the buffer pointed to by the pbData parameter.
		{
			printf("Size of the BLOB for the public key determined. %lu \n", dwBlobLen);
		}
		else
		{
			MyHandleError(TEXT("Error computing BLOB length."));
		}

		//-------------------------------------------------------------------
		// Allocate memory for the pbKeyBlob.
		pbKeyBlob = (BYTE*)malloc(dwBlobLen);
		if(pbKeyBlob == 0)
			MyHandleError(TEXT("Out of memory. \n"));

		//-------------------------------------------------------------------
		// Do the actual exporting into the key BLOB.

		if(!CryptExportKey(   
			hKey, 
			NULL,    
			PUBLICKEYBLOB,    
			0,    
			pbKeyBlob,    
			&dwBlobLen))
			MyHandleError(TEXT("Error during CryptExportKey."));

		//-------------------------------------------------------------------
		// Determine the size of the signature and allocate memory.

		dwSigLen= 0;
		if(CryptSignHash(
			hHash, 
			dwKeySpec,		// Identifies the private key to use from the provider's container. It can be AT_KEYEXCHANGE or AT_SIGNATURE.
			NULL, 
			0, 
			NULL, 
			&dwSigLen))		//inout, When the function returns, the DWORD value contains the number of bytes stored in the buffer.
		{
			printf("Signature length %d found.\n",dwSigLen);
		}
		else
		{
			MyHandleError(TEXT("Error during CryptSignHash."));
		}

		//-------------------------------------------------------------------
		// Allocate memory for the signature buffer.

		pbSignature = (BYTE *)malloc(dwSigLen);
		if(pbSignature == 0)
			MyHandleError(TEXT("Out of memory."));

		//-------------------------------------------------------------------
		// Sign the hash object. key's pin is required

		if(!CryptSignHash(
			hHash, 
			dwKeySpec,
			//AT_SIGNATURE, 
			NULL, 
			0, 
			pbSignature, 
			&dwSigLen))
			MyHandleError(TEXT("Error during CryptSignHash !!!"));

		//-------------------------------------------------------------------
		// Destroy the hash object.

		if(hHash) 
			CryptDestroyHash(hHash);

		printf("The signing phase of this program is completed.\n\n");

		//-------------------------------------------------------------------
		// Get the public key of the user who created the digital signature 
		// and import it into the CSP by using CryptImportKey. 
		// This returns a handle to the public key in hPubKey.

		if(!CryptImportKey(
			hCryptProv,
			pbKeyBlob,
			dwBlobLen,
			0,
			0,
			&hPubKey))	// out
			MyHandleError(TEXT("Public key import failed."));

		//-------------------------------------------------------------------
		// Create a new hash object.

		if(!calculateHashOperation(hCryptProv, hHash, pbBuffer, dwBufferLen))	return -1;

		//-------------------------------------------------------------------
		// Validate the digital signature.

		if(CryptVerifySignature(
			hHash,			// A handle to the hash object to verify.
			pbSignature,	// The address of the signature data to be verified.
			dwSigLen,		// The number of bytes in the pbSignature signature data.
			hPubKey,		// A handle to the public key to use to authenticate the signature
			NULL, 
			0)) 
		{
			printf("The signature has been verified.\n");
			printf("-------------------------------------------------------\n");
		}
		else
		{
			printf("Signature not validated!\n");
		}

		//-------------------------------------------------------------------
		// Free memory to be used to store signature.

		if(pbSignature)
			free(pbSignature);

		//-------------------------------------------------------------------
		// Destroy the hash object.

		if(hHash) 
			CryptDestroyHash(hHash);

		// Release the provider handle.
		if (!CryptReleaseContext(hCryptProv,0))
			printf("The handle could not be released.\n");

	} // end of bucle for

	//-------------------------------------------------------------------
	// Release the provider handle.
	if (!CryptReleaseContext(hMCryptProv,0))
		printf("The Mhandle could not be released.\n");

	return 0;
}
